﻿//************************************************************************************************************
// CoNatural.dll
// Author: Roger Torres
//************************************************************************************************************
// You can use, modify, and distribute the source code and executable programs based on the source code.
// This source code is provided "as is" and without warranties as to performance or merchantability. 
// The author and/or distributors of this source code may have made statements about this source code. 
// Any such statements do not constitute warranties and shall not be relied on by the user in deciding 
// whether to use this source code. This source code is provided without any express or implied warranties 
// whatsoever. Because of the diversity of conditions and hardware under which this source code may be used, 
// no warranty of fitness for a particular purpose is offered. The user is advised to test the source code 
// thoroughly before relying on it. The user must assume the entire risk of using the source code.
//************************************************************************************************************

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.CodeDom.Compiler;
using Microsoft.CSharp;

namespace CoNatural.Data.VisualStudio.AddIn.Tools {
	public class Compiler {
		static public Dictionary<string, string> GetLoadedAssemblies() {
			var loaded = new Dictionary<string, string>();
			foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) {
				try {
					// Ignore dynamic assemblies
					if (!assembly.ManifestModule.FullyQualifiedName.StartsWith("<"))
						loaded[assembly.GetName().Name] = assembly.Location;
				}
				catch { /* Ignore dynamic assemblies */ }
			}
			return loaded;
		}

		static public Assembly CompileAssembly(string source, params Type[] targetTypes) {
			Dictionary<string, string> loaded = GetLoadedAssemblies();
			foreach(Type type in targetTypes)
				loaded[type.Assembly.GetName().Name] = type.Assembly.Location;
			Assembly executing = Assembly.GetExecutingAssembly();
			loaded[executing.GetName().Name] = executing.Location;
			return Compile(source, loaded.Values.ToArray()).CompiledAssembly;
		}

		static public Type CompileType(string typeName, string source, params Type[] targetTypes) {
			return CompileAssembly(source, targetTypes).GetType(typeName, true);
		}

		static public CompilerResults Compile(string source, params string[] references) {
			CompilerParameters parameters = new CompilerParameters();
			parameters.ReferencedAssemblies.AddRange(references);
			parameters.GenerateExecutable = false;
			parameters.GenerateInMemory = true;
			parameters.IncludeDebugInformation = false;
			var providerOptions = new Dictionary<string, string>();
			if(Environment.Version.Major != 4)
				providerOptions.Add("CompilerVersion", "v3.5");
			//providerOptions.Add("CompilerVersion", "v4.0");
			CSharpCodeProvider provider = new CSharpCodeProvider(providerOptions);
			CompilerResults results = provider.CompileAssemblyFromSource(parameters, source);

			if (results.Errors.HasErrors) {
				// throw exception with errors
				StringBuilder sb = new StringBuilder();
				foreach (CompilerError error in results.Errors)
					sb.AppendLine(error.ToString());
				throw new Exception(sb.ToString());
			}

			return results;
		}
	}
}
